//
// lock_ptr.h
//
// A smart pointer to manage synchronized access to a shared resource.
//
// LockPtr provides a simple and concise syntax for accessing a
// shared resource. The LockPtr is a smart pointer and provides
// pointer operators -> and *. LockPtr does not have copy semantics and it is
// not intended to be stored in containers. Instead, instances of LockPtr are
// usually unamed or short-lived named variables.
//
// LockPtr uses an external lock, it acquires the lock in the constructor, and
// it guarantees the lock is released in the destructor.
//
// Since different types of locks have different method names, such as
// Enter/Exit or Lock/Unlock, etc, LockPtr uses an external customizable policy
// to bind to different operations. The external policy is a set of template
// functions that can be specialized for different types of locks, if needed.
// Think of this policy as an adapter between the lock type and the LockPtr.
//
// Usage: let's assume that we have the type below:
//
// class X {
//  public:
//    X() : i_(0) {}
//    void f() {}
//
//  private:
//    int i_;
//
//    friend int LockPtrTest(int, int);
// };
//
// We have an instance of this type and an external lock instance to serialize
// the access to the X instance.
//
// Using LockPtr, the code is:
//
//    X x;
//    LLock local_lock;
//
//    LockPtr<X>(x, local_lock)->f();
//
// For more example, please see the unit test of the module.

#ifndef LONGKEY_BASE_LOCK_PTR_H_
#define LONGKEY_BASE_LOCK_PTR_H_

template<typename T>
class LockPtr
{
public:
	template <typename U>
	LockPtr(T& obj, U& lock) : pobj_(&obj), plock_(&lock), punlock_method_(&LockPtr::Unlock<U>)
	{
		AcquireLock(lock);
	}

	~LockPtr()
	{
		ASSERT1(punlock_method_);
		(this->*punlock_method_)();
	}

	T& operator*()
	{
		ASSERT1(pobj_);
		return *pobj_;
	}

	T* operator->()
	{
		return pobj_;
	}

private:
	template<class U>
	void Unlock()
	{
		ASSERT1(plock_);
		U& lock = *(static_cast<U*>(plock_));
		ReleaseLock(lock);
	}

	T* pobj_;
	void* plock_;

	void (LockPtr::*punlock_method_)();

	DISALLOW_EVIL_CONSTRUCTORS(LockPtr);
};

#endif	// LONGKEY_BASE_LOCK_PTR_H_